package com.cherrypt.api.core.service.impl;

import com.cherrypt.api.common.constant.WebConst;
import com.cherrypt.api.common.enums.ResultEnum;
import com.cherrypt.api.common.exception.TipException;
import com.cherrypt.api.core.service.PostService;
import com.cherrypt.api.core.service.WaitService;
import com.cherrypt.api.model.dataobject.Post;
import com.cherrypt.api.model.dataobject.Wait;
import com.cherrypt.api.model.dto.WaitDto;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

@Service
public class WaitServiceImpl implements WaitService {
    private static final Logger logger = LoggerFactory.getLogger(WaitServiceImpl.class);

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private ValueOperations<String, Object> valueOperations;

    @Autowired
    private PostService postService;

    @Value(value = "${api.config.wait.time}")
    private long waitTime;

    @Value(value = "${api.config.wait.agree}")
    private int waitAgree;

    @Value(value = "${api.config.redis.wait.prefix}")
    private String redisWaitPrefix;

    /**
     * 首先查询Post的更新日期和Post是否需要候选，如果大于waitTime则把Post的is_enable设为true，
     * 并且删除掉redis中的agree记录返回错误
     * 如果小于，先检查Redis中当前Post的Agree - Disagree是否大于等于waitAgree
     * 如果大于则把Post的is_wait记录设为false并且删除掉redis中的agree记录
     * 如果小于则在redis中更新并增加Agree记录
     *
     * @param pid   pid
     * @param uid   uid
     * @param agree 票数
     */
    @Override
    public boolean agree(int pid, int uid, boolean agree) throws TipException {
        Post post = postService.getModifyAndWait(pid);
        if (post == null) {
            throw new TipException(ResultEnum.POST_ERROR);
        }
        // 查询redis中uid是否已候选
        if (getWait(pid, uid) != -1) {
            throw new TipException(ResultEnum.REPEAT_WAIT_ERROR);
        }
        if (!post.getIsEnable() || !post.getIsWait()) {
            throw new TipException(ResultEnum.POST_EMPTY_ERROR);
        } else if (post.getModify() + waitTime < System.currentTimeMillis()) {
            boolean isUpdateEnable = postService.updateEnable(pid, false);
            if (isUpdateEnable) {
                // 删除redis中所有该pid的agree记录
                removeAllAgreeKey(pid);
                logger.info("{} 候选 {} 过期", uid, pid);
            } else {
                throw new TipException(ResultEnum.WAIT_ERROR);
            }
        } else {
            int currentAgree = agree ? 1 : -1;
            Wait wait = getWaitCount(pid);
            // 大于则把Post的is_wait记录设为false并且删除掉redis中的agree记录
            if (wait.getAgree() - wait.getDisagree() + currentAgree >= waitAgree) {
                boolean isUpdateWait = postService.updateWait(pid, false);
                if (isUpdateWait) {
                    // 删除redis中所有该pid的agree记录
                    removeAllAgreeKey(pid);
                    logger.info("{} 达成候选 {}", uid, pid);
                } else {
                    throw new TipException(ResultEnum.WAIT_ERROR);
                }
            } else {
                valueOperations.set(redisWaitPrefix + pid +
                        WebConst.REDIS_REGEX + uid +
                        WebConst.REDIS_REGEX + agree, agree);
            }
        }
        return true;
    }

    private Integer getWait(int pid, int uid) {
        Set<String> keys = redisTemplate.keys(redisWaitPrefix + pid +
                WebConst.REDIS_REGEX + uid +
                WebConst.REDIS_REGEX + WebConst.REDIS_ALL_KEY);
        if (keys != null && keys.size() == 1) {
            boolean isAgree = (boolean) valueOperations.get(keys.iterator().next());
            return isAgree ? 1 : 0;
        } else {
            return -1;
        }
    }

    @Override
    public WaitDto getWaitList(Integer[] pids, int uid) {
        WaitDto waitDto = new WaitDto();
        List<Integer> waitList = new ArrayList<>();
        List<Wait> countList = new ArrayList<>();
        for (int pid : pids) {
            waitList.add(getWait(pid, uid));
            countList.add(getWaitCount(pid));
        }
        waitDto.setWaitList(waitList);
        waitDto.setCountList(countList);
        return waitDto;
    }

    private Wait getWaitCount(int pid) {
        Set<String> agreeKey = redisTemplate.keys(redisWaitPrefix + pid +
                WebConst.REDIS_REGEX + WebConst.REDIS_ALL_KEY + "true");
        Set<String> disagreeKey = redisTemplate.keys(redisWaitPrefix + pid +
                WebConst.REDIS_REGEX + WebConst.REDIS_ALL_KEY + "false");
        Wait wait = new Wait();
        if (agreeKey != null) {
            wait.setAgree(agreeKey.size());
        } else {
            wait.setAgree(0);
        }
        if (disagreeKey != null) {
            wait.setDisagree(disagreeKey.size());
        } else {
            wait.setDisagree(0);
        }
        return wait;
    }

    // 删除redis中所有该pid的agree记录
    private void removeAllAgreeKey(int pid) {
        Set<String> keys = redisTemplate.keys(redisWaitPrefix + pid + WebConst.REDIS_ALL_KEY);
        if (keys != null && keys.size() > 0) {
            keys.forEach(key -> {
                redisTemplate.delete(key);
            });
        }
    }
}
